package cn.lyjuan.aliyun.mq.conf.config;

import cn.lyjuan.aliyun.mq.conf.java.conf.MQAuthCst;
import cn.lyjuan.aliyun.mq.conf.java.conf.consumer.AMQConsumer;
import cn.lyjuan.aliyun.mq.conf.java.conf.consumer.MQConsumerImpl;
import cn.lyjuan.aliyun.mq.conf.java.conf.producer.AMQProducer;
import cn.lyjuan.aliyun.mq.conf.java.conf.producer.MQProducerImpl;
import cn.lyjuan.aliyun.mq.conf.java.dynamic.DynamicRegisterUtils;
import cn.lyjuan.aliyun.mq.conf.java.dynamic.MQUserCstDynamicImpl;
import com.aliyun.openservices.ons.api.MessageListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Properties;

/**
 * <p>
 * 根据配置文件加载MQ生产者或消费者<br />
 * 加载的Bean包括：<br />
 * 1. {@link cn.lyjuan.aliyun.mq.conf.java.conf.MQUserCst}配置类<br />
 * 属性由资源文件properties加载而来<br />
 * 2. {@link MQProducerImpl}生产者<br />
 * 3. {@link MQConsumerImpl}消费者<br />
 * 一个配置文件，只能加载生产者或消费者中的一个，由配置类的userType属性决定<br />
 * 配置类的isStart属性决定是否加载<br />
 * </p>
 * <p>
 * {@code uniqueBeanName}表示全局唯一的SpringBean名称前缀
 * </p>
 * <p>
 * 配置文件properties的路径分测试环境和生产环境<br />
 * 测试环境：加载类路径下的cst/{@code uniqueBaseName}Cst-test.properties<br />
 * 生产一起：加载系统路径下{{@code mqUserProFilePath}}/cst/{@code uniqueBaseName}Cst.properties，
 * {mqUserProFilePath}为启动命令行参数或系统环境变量(命令行优先级高)，也可实现{@code prodDynamicService}方法
 * 自己配置该环境变量<br />
 * </p>
 * Created by chad on 2016/10/12.
 */
@Configuration
public abstract class ADynamicConfig
{
    private static Logger log = LogManager.getLogger(ADynamicConfig.class.getName());

    public static final String VARIABLE             = "mq_user_conf";

    /**
     * 使用{mqUserProFilePath}指定配置文件的路径
     * 1.如果有命令行有该参数，则使用命令行的该参数的值
     * 2.如果命令行没有，则在环境变量中查找该变量
     * 3.如果都没有，则使用空字符串注入
     */
    public static final String MQ_USER_CONF_EXPRESS = "#{systemProperties.?[key eq '"+ VARIABLE +"'].size() > 0 " +
            "? systemProperties."+ VARIABLE +" : " +
            "(systemEnvironment.?[key eq '"+ VARIABLE +"'].size() > 0 ? systemEnvironment."+ VARIABLE +" : '')}";

    protected abstract String getUniqueBaseName();

    @Bean(name = MQAuthCst.QUITE_BEAN_NAME)
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public MQAuthCst mqAuthCst(@Value(MQ_USER_CONF_EXPRESS) String mqUserConf) throws IOException
    {
        String path         = null;
        if (StringUtils.isEmpty(mqUserConf))// 如果环境变量无值，则使用项目classpath
            path = getPathInClassPath(MQAuthCst.QUITE_BEAN_NAME);
        else
            path = apendPath(mqUserConf, MQAuthCst.QUITE_BEAN_NAME);

        return loadMQAuthCstByPro(path);
    }

    public ADynamicConfig(ConfigurableApplicationContext applicationContext,
                          @Value(MQ_USER_CONF_EXPRESS) String mqUserConf)
    {
        String baseFileName = this.getUniqueBaseName() + MQUserCstDynamicImpl.NAME_SUFFIX;
        String path         = null;
        if (StringUtils.isEmpty(mqUserConf))// 如果环境变量无值，则使用项目classpath
            path = getPathInClassPath(baseFileName);
        else
            path = apendPath(mqUserConf, baseFileName);

        log.debug("load {} properties: {}", this.getUniqueBaseName(), path);

        DynamicRegisterUtils.register(applicationContext, this.getUniqueBaseName(), path,
                this.mqProducerClass(),
                this.mqConsumerClass(),
                this.messageListenerClass());
    }

    /**
     * 加载消费者的消息监听器
     * 默认返回{@code null}
     *
     * @return
     */
    public Class<? extends MessageListener> messageListenerClass()
    {
        return null;
    }

    /**
     * 生产者类
     * @return
     */
    public Class<? extends AMQProducer> mqProducerClass(){return MQProducerImpl.class;}

    /**
     * 消费者类
     * @return
     */
    public Class<? extends AMQConsumer> mqConsumerClass(){return MQConsumerImpl.class;}

    /**
     * 获取项目下的配置文件
     *
     * @param baseName
     * @return
     */
    public String getPathInClassPath(String baseName)
    {
        String path = ADynamicConfig.class.getClassLoader().getResource("").getPath()
                + "/cst/" + baseName + "-test.properties";

        return path;
    }

    /**
     * 获取指定路径下的配置文件
     *
     * @param mqUserProFilePath
     * @param baseName
     * @return
     */
    public String apendPath(String mqUserProFilePath, String baseName)
    {
        String path = mqUserProFilePath + "/cst/" + baseName + ".properties";

        return path;
    }

    /**
     * 根据系统绝对路径加载{@link MQAuthCst}资源
     *
     * @param path
     * @return
     * @throws IOException
     */
    private MQAuthCst loadMQAuthCstByPro(String path) throws IOException
    {
        Properties mqAuthPro = PropertiesLoaderUtils.loadProperties(new FileSystemResource(path));

        MQAuthCst mqAuthCst = new MQAuthCst(mqAuthPro);

        return mqAuthCst;
    }
}
